function createElement(content) {
    let wrap = document.createElement('DIV');
    wrap.innerHTML = content;
    if (!wrap.firstElementChild) throw new TypeError(`after '${content}' is an illegal html tag string.`);
    return wrap.firstElementChild;
}

export let scopeStyles = {};

export let elementPrototype = typeof HTMLElement !== "undefined"
        ? HTMLElement.prototype : Element.prototype;

const auiFindSelectorAll = function (selector) {
    if (!this || this === null) {
        return;
    }
    if (/(^\s*|,\s*)>/.test(selector)) {
        if (!this.id) {
            this.id = 'ID_' + new Date().getTime();
            var removeId = true;
        }
        selector = selector.replace(/(^\s*|,\s*)>/g, '$1#' + this.id + ' >');
        var result = document.querySelectorAll(selector);
        if (removeId) {
            this.id = null;
        }
        return result;
    } else {
        return this.querySelectorAll(selector);
    }
};

const auiFindSelector = function (selector) {
    var res = this.auiFindSelectorAll(selector);
    if (res.length < 1) {
        return null;
    }
    return res[0];
};

const auiToggleAttribute = function(key, value) {
    if (this.hasAttribute(key)) {
       this.removeAttribute(key);
       return false;
    } else {
       this.setAttribute(key, value);
       return true;
    }
}

const auiHasClass = function(name) {
    if (this.classList) {
        return this.classList.contains(name);
    } else {
        return this.className.indexOf(name) > -1;
    }
};

const auiAddOnceClass = function(name) {
    if (this.classList) {
        if (!this.classList.contains(name)) {
            this.classList.add(name)
        }
    }
};

const auiRemoveOnceClass = function(name) {
    if (this.classList) {
        if (this.classList.contains(name)) {
            this.classList.remove(name)
        }
    }
};

const auiGetAttribute = function (name, defaultval) {
    var ret = this.getAttribute(name);
    if (ret === null) {
        if (defaultval) {
            return defaultval;
        }
        return null;
    }
    return ret;
};

const auiHeight = function() {
    return getComputedStyle(this).height;
}
    
const auiWidth = function() {
    return getComputedStyle(this).width;
}

/**
 * Removes an event listener from an element.
 */  
export const auiOff = function(evt, fn, opts = false) {
    this.removeEventListener(evt, fn, opts);
}


/**
 * Adds an event listener to an element with the ability to use event delegation.
 */
export const auiOn = function(evt, fn, opts = {}) {
    const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e);
    this.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false);
    if (opts.target) return delegatorFn;
  };

export function extendDomPrototpe(key, func, doc = window.document) {
    doc[key] = func;
    elementPrototype[key] = func;
}

export let getIEVersion = (function() {
    if (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) {
        return parseFloat( RegExp.$1 ); }
    else { return -1; }
});

/**
 * 添加一个style
 *
 * @param key
 * @param css
 * @param once
 */
export function addStyle(key, css, once = true) {
    if (!scopeStyles[key] && once) {
        let head = document.head || document.getElementsByTagName('head')[0];
        let style = document.createElement('style');
        let id = "scopedcss_" + key;

        style.id = id;
        style.type = 'text/css';
        if (style.styleSheet){
            style.styleSheet.cssText = css;
        } else {
            style.appendChild(document.createTextNode(css));
        }

        scopeStyles[key] = style;
        head.appendChild(style);
    }
}

/**
 * 添加一个link
 *
 * @param href
 * @param rel
 */
export function addLink(href, rel = "stylesheet") {
    let head = document.head || document.getElementsByTagName('head')[0];
    let link = document.createElement("link");
    link.rel = rel;
    link.href = href;
    head.appendChild(link);
}

var global_hack_img = new Image();
/**
 * 获得图片原始大小
 * 
 * @param {*} img 
 */
export function getNaturalSize(img){
    var naturalSize ={};
    if(img.naturalWidth && img.naturalHeight){
        naturalSize.width = img.naturalWidth;
        naturalSize.height = img.naturalHeight;
    }else{
        global_hack_img.src = img.src;
        naturalSize.width = global_hack_img.width;
        naturalSize.height = global_hack_img.height;
    }
    return naturalSize;
}

/** 
* Creates an element from a string 
*/
export function createElementByHTML() {
    const el = document.createElement('div');
    el.innerHTML = str;
    return el.firstElementChild;
}

/**
 * Returns true if the element specified is visible in the viewport, false otherwise.
 * 
 * @param {*} el 
 * @param {*} partiallyVisible 
 */
export const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
    const { top, left, bottom, right } = el.getBoundingClientRect();
    const { innerHeight, innerWidth } = window;
    return partiallyVisible
      ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
          ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
      : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
  };


/**
 * Returns the scroll position of the current page.
 */  
export const getScrollPosition = (el = window) => ({
    x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft,
    y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop
  });

/**
 * Creates a hash for a value using the SHA-256 algorithm. Returns a promise.
 */  
export const hashBrowser = val =>
  crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => {
    let hexes = [],
      view = new DataView(h);
    for (let i = 0; i < view.byteLength; i += 4)
      hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8));
    return hexes.join('');
  });  

/**
 * Returns a new MutationObserver and runs the provided callback for each mutation on the specified element.
 */    
export const observeMutations = (element, callback, options) => {
    const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m)));
    observer.observe(
      element,
      Object.assign(
        {
          childList: true,
          attributes: true,
          attributeOldValue: true,
          characterData: true,
          characterDataOldValue: true,
          subtree: true
        },
        options
      )
    );
    return observer;
  };
  
export const onUserInputChange = callback => {
    let type = 'mouse',
      lastTime = 0;
    const mousemoveHandler = () => {
      const now = performance.now();
      if (now - lastTime < 20)
        (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler);
      lastTime = now;
    };
    document.addEventListener('touchstart', () => {
      if (type === 'touch') return;
      (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler);
    });
  };

export const runAsync = fn => {
    const worker = new Worker(
      URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
        type: 'application/javascript; charset=utf-8'
      })
    );
    return new Promise((res, rej) => {
      worker.onmessage = ({ data }) => {
        res(data), worker.terminate();
      };
      worker.onerror = err => {
        rej(err), worker.terminate();
      };
    });
  };  

export const scrollToTop = () => {
    const c = document.documentElement.scrollTop || document.body.scrollTop;
    if (c > 0) {
      window.requestAnimationFrame(scrollToTop);
      window.scrollTo(0, c - c / 8);
    }
  };  

export const UUIDGeneratorBrowser = () => {
    return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => {
        return (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
    });  
}

function polyfillShear() {

    /**
     * 多行折断 
     * 
     * @param target 
     * @param line_count 
     * @param after_html 
     * 
     * @example 
     *  document.auiShear(document.getElementById('target'), 3);
     *  document.auishear(document.getElementById('target'),  3, '<span>（展开）</span>');
     */
    document.auiShear = function(target, line_count = 0, after_html = '') {
        let sel = window.getSelection();
    
        let full_html = target.innerHTML
        let full_text = target.textContent;
    
        sel.selectAllChildren(target);
        sel.collapseToStart();
    
        for (let i = 0; i < line_count; i++) {
            (sel).modify('extend', 'right', 'character');
            (sel).modify('extend', 'right', 'lineboundary');
            if (!target.contains(sel.getRangeAt(0).endContainer)) {
                sel.selectAllChildren(target);
                break;
            }
        }
    
        let cut_node = sel.getRangeAt(0).cloneContents();
        let wrap = document.createElement('DIV');
        wrap.appendChild(cut_node);
        let cut_html = wrap.innerHTML;
        let cut_text = cut_node.textContent;
    
        target.innerHTML = cut_html;
    
        sel.selectAllChildren(target);
        sel.collapseToEnd();
    
        let cut_html_with_after_html = '';
        let cut_text_with_after_html = '';
        let br = createElement('<br/>');
        if (after_html !== '') {
            let insert = createElement(after_html);
            target.appendChild(br);
            target.appendChild(insert);
            let rect = insert.getBoundingClientRect();
            for (let i = 0; i < 1000; i++) {
                (sel).modify('extend', 'left', 'character');
                if (sel.getRangeAt(0).getBoundingClientRect().width >= rect.width) {
                    sel.deleteFromDocument();
                    break;
                }
            }
            target.removeChild(br);
            cut_html_with_after_html = target.innerHTML;
            cut_text_with_after_html = target.textContent;
        }
    
        return {
            full_html,
            full_text,
            cut_html,
            cut_text,
            cut_html_with_after_html,
            cut_text_with_after_html
        }
    }
}

function polyfillDom(doc) {
    let _div = doc.createElement("div");
    doc.auiCreateDOM = function(html, attrs) {
        _div.innerHTML = html;
        let dom = _div.children[0];
        for (let key in attrs) {
            dom.setAttribute(key, attrs[key]);
        }
        return dom;
    }
}

export function polyfill(doc = window.document) {
    extendDomPrototpe("auiFindSelectorAll", auiFindSelectorAll, doc);
    extendDomPrototpe("auiFindSelector", auiFindSelector, doc);
    extendDomPrototpe("auiToggleAttribute", auiToggleAttribute, doc);
    extendDomPrototpe("auiHasClass", auiHasClass, doc);    
    extendDomPrototpe("auiAddOnceClass", auiAddOnceClass, doc);
    extendDomPrototpe("auiRemoveOnceClass", auiRemoveOnceClass, doc);
    extendDomPrototpe("auiGetAttribute", auiGetAttribute, doc);
    extendDomPrototpe("auiWidth", auiWidth, doc);    
    extendDomPrototpe("auiHeight", auiHeight, doc);    
    extendDomPrototpe("auiOn", auiOn, doc);
    extendDomPrototpe("auiOff", auiOff, doc);
    doc.auiIeVersion = getIEVersion();
    polyfillDom(doc);
}